#ifndef LONGKEY_PCAGENT_COM_WRAPPER_H_
#define LONGKEY_PCAGENT_COM_WRAPPER_H_

class AppBundle;
typedef shared_ptr<AppBundle> ControllingPtr;

// Generalizes the creation of COM wrappers for a given class T.
// It requires:
//   * The wrapper class TWrapper derives from ComWrapper
//   * The wrapped class T provides access to model instance
template <typename TWrapper, typename T>
class ComWrapper : public CComObjectRootEx<CComObjectThreadModel>
{
public:
	static HRESULT Create(const ControllingPtr& controlling_ptr,
		T* t, IDispatch** t_wrapper)
	{
		ASSERT1(t);
		ASSERT1(t_wrapper);

		ASSERT1(IsModelLockedByCaller(t->model()));

		scoped_ptr<TComObject> t_com_object;
		HRESULT hr = TComObject::CreateInstance(address(t_com_object));
		if (FAILED(hr)) return hr;

		hr = t_com_object->QueryInterface(t_wrapper);
		if (FAILED(hr)) return hr;

		t_com_object->model_ = t->model();
		t_com_object->controlling_ptr_ = controlling_ptr;
		t_com_object->wrapped_obj_ = t;

		t_com_object.release();
		return S_OK;
	}

protected:
	ComWrapper() : model_(NULL), wrapped_obj_(NULL) {}
	~ComWrapper() {}

	void FinalRelease()
	{
		controlling_ptr_.reset();
		wrapped_obj_ = NULL;
	}

	const Model* model() const 
	{
		return interlocked_exchange_pointer(&model_, model_);
	}

	const ControllingPtr& controlling_ptr() const
	{
		ASSERT1(IsModelLockedByCaller(wrapped_obj_->model()));
		return controlling_ptr_;
	}

	T* wrapped_obj() { return interlocked_exchange_pointer(&wrapped_obj_, wrapped_obj_); }

private:
	typedef CComObject<TWrapper> TComObject;

	// The pointer is written and read from multiple threads and it is written
	// to with the same value by the atomic pointer exchange, hence the volatile
	// and mutable cv qualifiers respectively.
	mutable Model* volatile model_;

	ControllingPtr controlling_ptr_;

	T* wrapped_obj_;

	DISALLOW_EVIL_CONSTRUCTORS(ComWrapper);
};

#endif	// LONGKEY_PCAGENT_COM_WRAPPER_H_